package server;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.net.SocketException;
import java.nio.ByteBuffer;
import java.util.HashMap;
import java.util.NoSuchElementException;
import java.util.StringTokenizer;

public class ClientHandler extends Thread {
	public static final String TYPE_REGISTER = "R";
	public static final String TYPE_MESSAGE = "M";
	public static final String TYPE_ANSWER = "A";
	public static final String TYPE_ERROR = "E";
	public static final String TYPE_LOGUOT = "L";

	private HashMap<String, Socket> mapClient;
	private Socket clientSocket = null;
	private String name;
	private InputStream is = null;
	private OutputStream os = null;

	public ClientHandler(Socket clientSocket, HashMap<String, Socket> mapClient) {
		this.clientSocket = clientSocket;
		synchronized (mapClient) {
			this.mapClient = mapClient;
		}
	}

	@Override
	public void run() {
		while (true) {
			try {
				if (is == null) {
					is = clientSocket.getInputStream();
				}
				byte[] bt = new byte[4];
				is.read(bt);
				int leng = ByteBuffer.wrap(bt).getInt();

				bt = new byte[leng];
				is.read(bt);
				String data = new String(bt);
				parseData(data);

			} catch (SocketException e1) {
				synchronized (mapClient) {
					mapClient.remove(name);
				}
				// sendError("Doi tac da thoat");
				break;
			} catch (Exception e) {
				e.printStackTrace();
				try {
					if (is != null) {
						is.close();
					}
					if (clientSocket != null)
						clientSocket.close();
					break;
				} catch (IOException e1) {
					e1.printStackTrace();
				}
			}
		}
	}

	public void parseData(String data) {
		if (data == null || data.length() == 0) {
			return;
		}
		String type;
		String source;
		String destination;
		String content;
		try {
			StringTokenizer st = new StringTokenizer(data, ";");
			type = st.nextToken();
			source = st.nextToken();
			destination = st.nextToken();
			content = st.nextToken();
		} catch (NoSuchElementException e) {
			sendAnswer("Gui tin nhan bi loi");
			return;
		}
		if (type.equals(ClientHandler.TYPE_REGISTER)) {
			synchronized (mapClient) {
				mapClient.put(source, clientSocket);
				this.name = source;
			}
			sendAnswer("Ghi danh thanh cong");
		} else if (type.equals(ClientHandler.TYPE_LOGUOT)) {
			if (clientSocket != null) {
				try {
					clientSocket.close();
					mapClient.remove(name);
					System.out.println(name + " da logout");
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
			mapClient.remove(clientSocket);
		} else if (type.equals(ClientHandler.TYPE_MESSAGE)) {
			Socket desSocket = mapClient.get(destination);
			if (desSocket == null || desSocket.isClosed()) {
				sendError("Dich den dang offline");
			} else {
				sendData(desSocket, source, content);
			}
		}
	}

	public byte[] encode(String data) {
		int leng = data.length();
		byte[] btLeng = ByteBuffer.allocate(4).putInt(leng).array();
		byte[] btData = data.getBytes();
		byte[] btResult = new byte[leng + 4];
		System.arraycopy(btLeng, 0, btResult, 0, 4);
		System.arraycopy(btData, 0, btResult, 4, leng);
		return btResult;
	}

	public void sendAnswer(String content) {
		String type = ClientHandler.TYPE_ANSWER;
		String source = "Server";
		StringBuffer sb = new StringBuffer();
		sb.append(type).append(";").append(source).append(";").append(content);
		byte[] btData = encode(sb.toString());
		if (os == null) {
			try {
				os = clientSocket.getOutputStream();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		try {
			os.write(btData);
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	public void sendError(String content) {
		String type = ClientHandler.TYPE_ERROR;
		String source = "Server";
		StringBuffer sb = new StringBuffer();
		sb.append(type).append(";").append(source).append(";").append(content);
		byte[] btData = encode(sb.toString());
		if (os == null) {
			try {
				os = clientSocket.getOutputStream();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		try {
			os.write(btData);
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	public void sendData(Socket desSocket, String src, String content) {
		String type = ClientHandler.TYPE_MESSAGE;
		StringBuffer sb = new StringBuffer();
		sb.append(type).append(";").append(src).append(";").append(content);
		byte[] bt = encode(sb.toString());
		try {
			OutputStream os = desSocket.getOutputStream();
			if (desSocket.isClosed()) {
				sendAnswer("Doi tac da offline");
				if (os != null) {
					os.close();
				}
				mapClient.remove(name);
			}
			os.write(bt);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}
